package com.jumi.microservice.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.jumi.microservice.common.exception.BusinessException;
import com.jumi.microservice.common.utils.ObjectUtils;
import com.jumi.microservice.constant.ExceptionEnum;
import com.jumi.microservice.constant.LogOperationTypeEnum;
import com.jumi.microservice.constant.StatusEnum;
import com.jumi.microservice.constant.UserAccountEnum;
import com.jumi.microservice.domain.*;
import com.jumi.microservice.domain.dto.AccountDTO;
import com.jumi.microservice.domain.dto.LogDTO;
import com.jumi.microservice.domain.dto.LoginLogDTO;
import com.jumi.microservice.domain.vo.AccountQuery;
import com.jumi.microservice.domain.vo.LoginLogQuery;
import com.jumi.microservice.domain.vo.OperateLogQuery;
import com.jumi.microservice.mapper.AccountMapper;
import com.jumi.microservice.mapper.AccountRoleRelationshipMapper;
import com.jumi.microservice.mapper.DepartmentMapper;
import com.jumi.microservice.mapper.LogMapper;
import com.jumi.microservice.service.AccountService;
import com.jumi.microservice.utils.SnowflakeIdWorker;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;


/**
 * 账号管理service组件
 *
 * @author Administrator
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class AccountServiceImpl implements AccountService {

    /**
     * 账号管理mapper组件
     */
    @Autowired
    private AccountMapper accountMapper;

    /**
     * 账号角色关系管理模块mapper组件
     */
    @Autowired
    private AccountRoleRelationshipMapper accountRoleRelationshipMapper;

    /**
     * 部门管理mapper组件
     */
    @Autowired
    private DepartmentMapper departmentMapper;

    /**
     * 日志管理mapper组件
     */
    @Autowired
    private LogMapper logMapper;

    /**
     * 分页查询账号
     *
     * @param accountQuery 查询条件
     * @return 账号DTO集合
     */
    @Override
    public List<AccountDTO> listAccountByPage(AccountQuery accountQuery) {
        ExceptionEnum.FAIL_NULL.doThrowIf(accountQuery == null);

        Long departmentId = accountQuery.getDepartmentId();

        LambdaQueryWrapper<Account> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(Account::getIsDel, StatusEnum.STATUS_NO.getCode());
        wrapper.eq(departmentId != null && departmentId != 1, Account::getDepartmentId, departmentId);
        wrapper.eq(accountQuery.getStatus() != null, Account::getStatus, accountQuery.getStatus());
        wrapper.like(StringUtils.isNotEmpty(accountQuery.getUserAccount()), Account::getUserAccount, accountQuery.getUserAccount());
        wrapper.like(StringUtils.isNotEmpty(accountQuery.getUserName()), Account::getUserName, accountQuery.getUserName());
        wrapper.ge(StringUtils.isNotEmpty(accountQuery.getCreateTimeFrom()), Account::getCreateTime, accountQuery.getCreateTimeFrom());
        wrapper.le(StringUtils.isNotEmpty(accountQuery.getCreateTimeTo()), Account::getCreateTime, accountQuery.getCreateTimeTo());
        wrapper.ge(StringUtils.isNotEmpty(accountQuery.getUpdateTimeFrom()), Account::getUpdateTime, accountQuery.getUpdateTimeFrom());
        wrapper.le(StringUtils.isNotEmpty(accountQuery.getUpdateTimeTo()), Account::getUpdateTime, accountQuery.getUpdateTimeTo());
        wrapper.orderByDesc(Account::getId, Account::getUpdateTime);

        List<Account> accounts = accountMapper.selectList(wrapper);
        List<AccountDTO> accountDtoList = convertAccountDoList2DtoList(accounts);

        Iterator<AccountDTO> iterator = accountDtoList.iterator();
        while (iterator.hasNext()) {
            AccountDTO accountDto = iterator.next();
            accountDto.setPassword(null);
            StringBuilder stringBuilder = new StringBuilder();
            String departmentName = getDepartmentName(accountDto.getDepartmentId(), stringBuilder);
            accountDto.setDepartmentName(departmentName);
            accountDto.setRoleName(accountRoleRelationshipMapper.getRoleNameByAccountId(accountDto.getId()));

            if (!StringUtils.isBlank(accountQuery.getRoleName())) {
                if (!StringUtils.isBlank(accountDto.getRoleName())) {
                    if (!accountDto.getRoleName().contains(accountQuery.getRoleName())) {
                        iterator.remove();
                    }
                } else {
                    iterator.remove();
                }
            }
            AccountLogin accountLogin = logMapper.getLoginNumAndLastLoginTimeByAccountId(accountDto.getId());
            accountDto.setLoginNum(accountLogin.getLoginNum());
            accountDto.setLastLoginTime(accountLogin.getLastLoginTime());
        }
        return accountDtoList;
    }

    /**
     * 根据部门ID递归查询父部门名称和自己部门名称的拼串
     *
     * @param id 部门ID
     * @return 部门名称的拼串
     */
    private String getDepartmentName(Long id, StringBuilder stringBuilder) {
        Department department = departmentMapper.selectById(id);

        if (stringBuilder.length() == 0) {
            stringBuilder.append(department.getDepartmentName());
        } else {
            if (department.getParentId() != 0) {
                stringBuilder.insert(0, department.getDepartmentName() + "-");
            }
        }
        if (department.getParentId() != 0) {
            getDepartmentName(department.getParentId(), stringBuilder);
        }
        return stringBuilder.toString();
    }


    /**
     * 根据账号id查询账号
     *
     * @param id 账号id
     * @return 账号
     */
    @Override
    public AccountDTO getAccountById(Long id) {
        ExceptionEnum.FAIL_ID_NULL.doThrowIf(id == null);

        Account account = accountMapper.selectById(id);
        ExceptionEnum.FAIL_NULL.doThrowIf(account == null);

        AccountDTO accountDTO = convertAccountDo2Dto(account);
        accountDTO.setPassword(null);
        List<Object> roleRelations = accountRoleRelationshipMapper.selectObjs(
                Wrappers.<AccountRoleRelationship>lambdaQuery()
                        .select(AccountRoleRelationship::getRoleId)
                        .eq(AccountRoleRelationship::getAccountId, id));

        if (roleRelations.size() > 0) {
            List<Long> ids = new ArrayList<>(roleRelations.size());
            for (Object o : roleRelations) {
                ids.add(Long.parseLong(String.valueOf(o)));
            }
            accountDTO.setRoleRelations(ids);
        }
        return accountDTO;
    }

    /**
     * 新增账号
     *
     * @param account 账号
     */
    @Override
    public void saveAccount(AccountDTO account, HttpServletRequest request) {
        ExceptionEnum.FAIL_NULL.doThrowIf(account == null);
        checkAccount(account);
        Account accountDO = convertAccountDto2Do(account);

        SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(1, 1);
        Long uid = snowflakeIdWorker.nextId();
        accountDO.setId(uid);

        if (!StringUtils.isBlank(accountDO.getPassword())) {
            String salt = generateSalt();
            String password = DigestUtils.md5DigestAsHex(accountDO.getPassword().concat(salt).getBytes());
            accountDO.setSalt(salt);
            accountDO.setPassword(password);
        }
        ExceptionEnum.FAIL_UID_NULL.doThrowIf(request.getHeader("uid") == null);
        Account admin = accountMapper.selectById(Long.parseLong(request.getHeader("uid")));

        LocalDateTime localDateTime = LocalDateTime.now();
        accountDO.setCreateTime(localDateTime);
        accountDO.setUpdateTime(localDateTime);
        accountDO.setCreater(admin.getUserName());
        accountDO.setModifier(admin.getUserName());
        int count = accountMapper.insert(accountDO);

        if (count != 0) {
            account.setId(accountDO.getId());
            saveRelations(account);
        }
    }

    /**
     * 随即生成盐
     *
     * @return salt 盐
     */
    private String generateSalt() {
        return RandomStringUtils.randomAlphanumeric(20);
    }

    /**
     * 更新账号
     *
     * @param account 账号
     */
    @Override
    public void updateAccount(AccountDTO account, HttpServletRequest request) {
        ExceptionEnum.FAIL_NULL.doThrowIf(account == null || account.getId() == null);
        checkAccount(account);
        Account accountDO = convertAccountDto2Do(account);

        if (!StringUtils.isBlank(accountDO.getPassword())) {
            String salt = generateSalt();
            String password = DigestUtils.md5DigestAsHex(accountDO.getPassword().concat(salt).getBytes());
            accountDO.setSalt(salt);
            accountDO.setPassword(password);
        }
        String uid = request.getHeader("uid");
        ExceptionEnum.FAIL_UID_NULL.doThrowIf(uid == null);
        Account admin = accountMapper.selectById(Long.parseLong(uid));
        accountDO.setModifier(admin.getUserName());
        accountDO.setUpdateTime(LocalDateTime.now());

        int count = accountMapper.updateById(accountDO);
        if (count != 0) {
            List<Long> roleRelations = account.getRoleRelations();

            if (roleRelations != null && roleRelations.size() > 0) {
                accountRoleRelationshipMapper.delete(
                        Wrappers.<AccountRoleRelationship>lambdaQuery()
                                .eq(AccountRoleRelationship::getAccountId, accountDO.getId())
                );
                saveRelations(account);
            }
        }
    }

    private void checkAccount(AccountDTO account) {
        List<Object> accountIdList = accountMapper.selectObjs(
                Wrappers.<Account>lambdaQuery()
                        .select(Account::getId)
                        .eq(Account::getUserAccount, account.getUserAccount())
                        .eq(Account::getIsDel, StatusEnum.STATUS_NO.getCode()));

        if (accountIdList.size() > 0) {
            throw new BusinessException(UserAccountEnum.USER_HAS_EXISTED);
        }

        List<Object> objectList = accountMapper.selectObjs(
                Wrappers.<Account>lambdaQuery()
                        .select(Account::getId)
                        .eq(Account::getMobile, account.getMobile())
                        .eq(Account::getIsDel, StatusEnum.STATUS_NO.getCode()));

        if (objectList.size() > 0) {
            throw new BusinessException(UserAccountEnum.PHONE_HAS_EXISTED);
        }
    }

    /**
     * 删除账号
     *
     * @param ids 账号ids,支持批量删除,账号ID以逗号分割
     */
    @Override
    public void removeAccountById(String ids, HttpServletRequest request) {
        ExceptionEnum.FAIL_ID_NULL.doThrowIf(ids == null);

        List<Account> accounts = accountMapper.selectList(
                Wrappers.<Account>lambdaQuery()
                        .select(Account::getStatus)
                        .eq(Account::getIsDel, StatusEnum.STATUS_NO.getCode())
                        .apply("id in (" + ids + ")"));

        for (Account account : accounts) {
            if (account.getStatus().equals(StatusEnum.STATUS_YES.getCode())) {
                throw new BusinessException(UserAccountEnum.USER_STATUS_ABLED);
            }
        }
        String uid = request.getHeader("uid");
        ExceptionEnum.FAIL_UID_NULL.doThrowIf(uid == null);
        Account admin = accountMapper.selectById(Long.parseLong(uid));

        LambdaUpdateWrapper<Account> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(Account::getIsDel, StatusEnum.STATUS_YES.getCode())
                .set(Account::getUpdateTime, LocalDateTime.now())
                .set(Account::getModifier, admin.getUserName())
                .in(Account::getId, Arrays.asList(ids.split(",")));
        accountMapper.update(new Account(), updateWrapper);
    }

    /**
     * 账号状态启用/停用
     *
     * @param id     主键id
     * @param status 状态 (1启用，0.禁用)
     */
    @Override
    public void enableStatus(Long id, Integer status, HttpServletRequest request) {
        Account account = accountMapper.selectById(id);
        ExceptionEnum.FAIL_NULL.doThrowIf(account == null);

        String uid = request.getHeader("uid");
        ExceptionEnum.FAIL_UID_NULL.doThrowIf(uid == null);
        Account admin = accountMapper.selectById(Long.parseLong(uid));

        account.setStatus(status);
        account.setModifier(admin.getUserName());
        account.setUpdateTime(LocalDateTime.now());
        accountMapper.updateById(account);
    }


    /**
     * 登录日次数志分页查询
     *
     * @param loginLogQuery 查询条件
     * @return LoginLogDTO 登录日志DTO列表
     */
    @Override
    public List<LoginLogDTO> listAccountLoginByPage(LoginLogQuery loginLogQuery) {
        ExceptionEnum.FAIL_NULL.doThrowIf(loginLogQuery == null);

        LambdaQueryWrapper<Log> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(loginLogQuery.getAccountId() != null, Log::getAccountId, loginLogQuery.getAccountId());
        wrapper.eq(Log::getIsDel, StatusEnum.STATUS_NO.getCode());
        wrapper.eq(Log::getOperateType, LogOperationTypeEnum.OPERATION_ADD.getCode());
        wrapper.ge(StringUtils.isNotEmpty(loginLogQuery.getOperateTimeFrom()), Log::getOperateTime, loginLogQuery.getOperateTimeFrom());
        wrapper.le(StringUtils.isNotEmpty(loginLogQuery.getOperateTimeTo()), Log::getOperateTime, loginLogQuery.getOperateTimeTo());
        wrapper.orderByDesc(Log::getOperateTime);

        List<LoginLogDTO> accountDtoList = convertAccountLogDoList2DtoList(logMapper.selectList(wrapper));
        return accountDtoList;
    }

    /**
     * 操作日志分页查询
     *
     * @param operateLogQuery 查询条件
     * @return LoginLogDTO 登录日志DTO列表
     */
    @Override
    public List<LogDTO> listAccountOperateLogByPage(OperateLogQuery operateLogQuery) {
        ExceptionEnum.FAIL_NULL.doThrowIf(operateLogQuery == null);

        LambdaQueryWrapper<Log> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(operateLogQuery.getAccountId() != null, Log::getAccountId, operateLogQuery.getAccountId());
        wrapper.eq(Log::getIsDel, StatusEnum.STATUS_NO.getCode());
        wrapper.like(StringUtils.isNotEmpty(operateLogQuery.getDepartmentName()), Log::getDepartmentName, operateLogQuery.getDepartmentName());
        wrapper.like(StringUtils.isNotEmpty(operateLogQuery.getUserName()), Log::getUserName, operateLogQuery.getUserName());
        wrapper.like(StringUtils.isNotEmpty(operateLogQuery.getMobile()), Log::getMobile, operateLogQuery.getMobile());
        wrapper.eq(operateLogQuery.getLogOperationType() != null, Log::getOperateType, operateLogQuery.getLogOperationType());
        wrapper.like(StringUtils.isNotEmpty(operateLogQuery.getAction()), Log::getOperateAction, operateLogQuery.getAction());
        wrapper.ge(StringUtils.isNotEmpty(operateLogQuery.getOperateTimeFrom()), Log::getOperateTime, operateLogQuery.getOperateTimeFrom());
        wrapper.le(StringUtils.isNotEmpty(operateLogQuery.getOperateTimeTo()), Log::getOperateTime, operateLogQuery.getOperateTimeTo());
        wrapper.orderByDesc(Log::getOperateTime);

        List<LogDTO> operateLogDtoList = convertOperateLogDoList2DtoList(logMapper.selectList(wrapper));
        return operateLogDtoList;
    }


    /**
     * 将账号DTO对象转换为DO对象
     *
     * @param accountDTO 账号DTO
     * @return Account 账号DO
     */
    private Account convertAccountDto2Do(AccountDTO accountDTO) {
        Account account = null;
        try {
            account = accountDTO.clone(Account.class);
        } catch (Exception e) {
            ExceptionEnum.FAIL_CHANGE.newException();
        }
        return account;
    }

    /**
     * 将账号DO对象转换为DTO对象
     *
     * @param account 账号DO
     * @return AccountDTO 账号DTO
     */
    private AccountDTO convertAccountDo2Dto(Account account) {
        AccountDTO accountDTO = null;
        try {
            accountDTO = account.clone(AccountDTO.class);
        } catch (Exception e) {
            ExceptionEnum.FAIL_CHANGE.newException();
        }
        return accountDTO;
    }


    /**
     * 将账号DO对象集合转换为DTO对象集合
     *
     * @param accounts 账号DO集合
     * @return List<AccountDTO> 账号DTO对象集合
     */
    private List<AccountDTO> convertAccountDoList2DtoList(List<Account> accounts) {
        List<AccountDTO> accountDtoList = null;
        try {
            accountDtoList = ObjectUtils.convertList(accounts, AccountDTO.class);
        } catch (Exception e) {
            ExceptionEnum.FAIL_CHANGE.newException();
        }
        return accountDtoList;
    }

    /**
     * 将登录次数日志DO对象集合转换为DTO对象集合
     *
     * @param logs 账号登录日志DO 集合
     * @return List<LoginLogDTO> 账号登录日志DTO对象集合
     */
    private List<LoginLogDTO> convertAccountLogDoList2DtoList(List<Log> logs) {
        List<LoginLogDTO> loginLogDtoList = null;
        try {
            loginLogDtoList = ObjectUtils.convertList(logs, LoginLogDTO.class);
        } catch (Exception e) {
            ExceptionEnum.FAIL_CHANGE.newException();
        }
        return loginLogDtoList;
    }

    /**
     * 将操作日志DO对象集合转换为DTO对象集合
     *
     * @param logs 账号登录日志DO 集合
     * @return List<LogDTO> 账号登录日志DTO对象集合
     */
    private List<LogDTO> convertOperateLogDoList2DtoList(List<Log> logs) {
        List<LogDTO> logDtoList = null;
        try {
            logDtoList = ObjectUtils.convertList(logs, LogDTO.class);
        } catch (Exception e) {
            ExceptionEnum.FAIL_CHANGE.newException();
        }
        return logDtoList;
    }


    /**
     * 新增关联关系
     *
     * @param account 账号
     */
    private void saveRelations(AccountDTO account) {
        LocalDateTime localDateTime = LocalDateTime.now();
        if (account.getRoleRelations() != null) {
            for (Long roleId : account.getRoleRelations()) {
                AccountRoleRelationship accountRoleRelationship = new AccountRoleRelationship();
                accountRoleRelationship.setAccountId(account.getId());
                accountRoleRelationship.setRoleId(roleId);
                accountRoleRelationship.setCreateTime(localDateTime);
                accountRoleRelationship.setUpdateTime(localDateTime);
                accountRoleRelationshipMapper.insert(accountRoleRelationship);
            }
        }
    }
}
